/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	ComponentModelRenderer.cpp
*
*	Comments	-	See ComponentModelRenderer.h
*
**************************************************************************************/
#include "../Include/ComponentModelRenderer.h"
#include "../Include/Actor.h"
#include "../Include/Engine.h"
#include "../Include/HashTable.h"
#include "../Include/Renderer.h"
#include "../Include/RenderPathLightPrePass.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/GlobalGraphicsStrings.h"

#include "../Include/EventRender.h"
#include "../Include/EventTransform.h"
#include "../Include/EventModelLoaded.h"
#include "../Include/EventModelUnload.h"

#include "../Include/RenderEntryModel.h"
#include "../Include/RenderEntryBindParameters.h"
#include "../Include/ParameterBinderMatrixRef.h"

namespace Pulse
{
	ComponentModelRenderer::ComponentModelRenderer( Actor *pOwner )
		: IComponent( pOwner ), m_pModel( PSX_NULL )
	{
	}

	ComponentModelRenderer::~ComponentModelRenderer( void )
	{
		m_pOwner->RemoveListener( EEvent::MODEL_LOADED, this, &ComponentModelRenderer::OnModelLoaded );
		m_pOwner->RemoveListener( EEvent::MODEL_UNLOAD, this, &ComponentModelRenderer::OnModelUnloaded );
		m_pOwner->RemoveListener( EEvent::RENDER, this, &ComponentModelRenderer::OnRender );
		//m_pOwner->RemoveListener( EEvent::TRANSFORM, this, &ComponentModelRenderer::OnTransform );

		m_camSpecificREList.Clear();
		ReleaseModel();
	}

	EErrorCode::Type ComponentModelRenderer::Initialize( XMLElementPtr pElem ) 
	{
		m_pOwner->AddListener( EEvent::MODEL_LOADED, this, &ComponentModelRenderer::OnModelLoaded );
		m_pOwner->AddListener( EEvent::MODEL_UNLOAD, this, &ComponentModelRenderer::OnModelUnloaded );
		m_pOwner->AddListener( EEvent::RENDER, this, &ComponentModelRenderer::OnRender );
		//m_pOwner->AddListener( EEvent::TRANSFORM, this, &ComponentModelRenderer::OnTransform );

		return EErrorCode::OKAY; 
	}

	void ComponentModelRenderer::Update( FLOAT dt )
	{

	}

	RenderEntryBindParameters * ComponentModelRenderer::GetCameraSpecificParameters( const ComponentCamera *pCamera )
	{
		CamSpecificREList::Iterator iter = m_camSpecificREList.Find(pCamera);

		if ( iter != m_camSpecificREList.IteratorEnd() )
		{
			return &iter->second;
		}
		else
		{
			RenderEntryBindParameters *pRE = &m_camSpecificREList[pCamera];
			pRE->SetStage( ERenderViewStage::MAIN );
			pRE->SetStageStep( ERenderViewStageStep::PROCESS );

			return pRE;
		}
	}

	EErrorCode::Type ComponentModelRenderer::RebuildRenderEntry( void )
	{
		// Release built render entries first.
		m_renderEntries.ClearRenderEntries();

		//
		RenderPathForwardPlus *pRenderPath = Engine::GetInstance()->GetRenderer()->GetRenderPath();
		
		// Set world transform ref.
		{
			ParameterSystem *pParamSys = Engine::GetInstance()->GetRenderer()->GetParameterSystem();
			IParameter *pParamTransform = pParamSys->GetMatrix( GFX_PARAM_TRANSFORM_WORLD );

			// Clear parameter first to make sure we set the right one
			m_pModel->RemoveParameter( GFX_PARAM_TRANSFORM_WORLD ); 

			m_pBinderTransform = dynamic_cast<ParameterBinderMatrixRef*>(m_pModel->AddParameter( pParamTransform ));
			m_pBinderTransform->SetAsMatrix( m_pOwner->GetTransformWorld() );
		}

		//EErrorCode::Type retCode = pRenderPath->BuildRenderEntries( m_pModel, &m_renderEntries );
		EErrorCode::Type retCode = pRenderPath->BuildRenderEntries( m_pModel, this );

		if ( EErrorCode::OKAY != retCode )
		{
			return retCode;
		}

		return EErrorCode::OKAY;
	}

	void ComponentModelRenderer::ReleaseModel( void )
	{
		m_pBinderTransform = PSX_NULL;
		PSX_SafeRelease( m_pModel );
	}

	void ComponentModelRenderer::OnRender( IEvent *pEvent )
	{
		if ( m_pModel && pEvent->GetEventType() == EEvent::RENDER )
		{
			EventRender *pRenderEvent = (EventRender*)pEvent;
			ComponentCamera *pCamera = pRenderEvent->GetCamera();
			RenderView *pView = pRenderEvent->GetRenderView();

			// Add any camera specific parameters before the render entries of the model
			// TODO: Hash the pointer address and cache it once(HashedString?) then do a 
			//	direct access in subsequent frames to improve performance.
			CamSpecificREList::Iterator iter = m_camSpecificREList.Find( pCamera );
			if ( iter != m_camSpecificREList.IteratorEnd() )
			{
				pView->AddRenderEntry( &iter->second );
			}
			else
			{
				RenderEntryBindParameters *pRE = &m_camSpecificREList[pCamera];
				pRE->SetStage( ERenderViewStage::MAIN );
				pRE->SetStageStep( ERenderViewStageStep::PROCESS );
			}

			const SIZE_T numEntries = m_renderEntries.GetNumEntries();
			RenderEntry **pEntries = m_renderEntries.GetRenderEntries();
			
			for ( SIZE_T i = 0; i < numEntries; ++i )
			{
				pView->AddRenderEntry( *(pEntries + i) );
			}
		}
	}

	void ComponentModelRenderer::OnTransform( IEvent *pEvent )
	{
		EventTransform *pTransform = (EventTransform*)pEvent;

		if ( pTransform->GetEventOwner() == this )
		{
			//m_pModel->SetTransform( pTransform->GetTransform() );
		}
	}

	void ComponentModelRenderer::OnModelLoaded( IEvent *pEvent )
	{
		EventModelLoaded *pNewModel = (EventModelLoaded*)pEvent;

		ReleaseModel();
		m_pModel = pNewModel->GetModel();

		if ( m_pModel )
		{
			EErrorCode::Type retCode = RebuildRenderEntry();

			if ( retCode != EErrorCode::OKAY )
			{
				PSX_PushError( "Failed to build render entries for the newly loaded model." );
			}
		}
	}

	void ComponentModelRenderer::OnModelUnloaded( IEvent *pEvent )
	{
		ReleaseModel();
	}

}